#pragma once

#include <gtest/gtest.h>
#include <iostream>
#include <string>

using namespace std;

enum TOKEN_TYPE
{
  TYPE_NUMBER,
  TYPE_UNIT,
  TYPE_NEGATIVE
};

struct Token
{
  string        StringValue;
  int            Value;
  TOKEN_TYPE    Type;
};

class Parer {
public:
  Parer() {
    sTokenTable[0] = { "zero",        0, TYPE_NUMBER };
    sTokenTable[1] = { "one",         1, TYPE_NUMBER };
    sTokenTable[2] = { "two",         2, TYPE_NUMBER };
    sTokenTable[3] = { "three",       3, TYPE_NUMBER };
    sTokenTable[4] = { "four",        4, TYPE_NUMBER };
    sTokenTable[5] = { "five",        5, TYPE_NUMBER };
    sTokenTable[6] = { "six",         6, TYPE_NUMBER };
    sTokenTable[7] = { "seven",       7, TYPE_NUMBER };
    sTokenTable[8] = { "eight",       8, TYPE_NUMBER };
    sTokenTable[9] = { "nine",        9, TYPE_NUMBER };
    sTokenTable[10] = { "ten",        10, TYPE_NUMBER };
    sTokenTable[11] = { "eleven",     11, TYPE_NUMBER };
    sTokenTable[12] = { "twelve",     12, TYPE_NUMBER };
    sTokenTable[13] = { "thirteen",   13, TYPE_NUMBER };
    sTokenTable[14] = { "fourteen",   14, TYPE_NUMBER };
    sTokenTable[15] = { "fifteen",    15, TYPE_NUMBER };
    sTokenTable[16] = { "sixteen",    16, TYPE_NUMBER };
    sTokenTable[17] = { "seventeen",  17, TYPE_NUMBER };
    sTokenTable[18] = { "eighteen",   18, TYPE_NUMBER };
    sTokenTable[19] = { "nineteen",   19, TYPE_NUMBER };
    sTokenTable[20] = { "twenty",     20, TYPE_NUMBER };
    sTokenTable[21] = { "thirty",     30, TYPE_NUMBER };
    sTokenTable[22] = { "forty",      40, TYPE_NUMBER };
    sTokenTable[23] = { "fifty",      50, TYPE_NUMBER };
    sTokenTable[24] = { "sixty",      60, TYPE_NUMBER };
    sTokenTable[25] = { "seventy",    70, TYPE_NUMBER };
    sTokenTable[26] = { "eighty",     80, TYPE_NUMBER };
    sTokenTable[27] = { "ninety",     90, TYPE_NUMBER };
    sTokenTable[28] = { "hundred",     100, TYPE_UNIT };
    sTokenTable[29] = { "thousand",  10000, TYPE_UNIT };
    sTokenTable[30] = { "million", 1000000, TYPE_UNIT };
    sTokenTable[31] = { "negative", -1, TYPE_NEGATIVE };
  }

  bool GetPreToken(const string& value, int& pos, Token& nextToken)
  {
    bool bRetValue = false;
    pos = value.find_last_not_of(" ", pos);
    if (pos == string::npos)
    {
      return false;
    }

    string tokenStr;
    string::size_type next = value.rfind(" ", pos);
    if (next != string::npos)
    {
      tokenStr = value.substr(next + 1, pos - next);
    }
    else
    {
      tokenStr = value.substr(0, pos + 1);
    }

    for (int i = 0; i < sizeof(sTokenTable) / sizeof(sTokenTable[0]); ++i)
    {
      if (tokenStr.compare(sTokenTable[i].StringValue) == 0)
      {
        nextToken.Type = sTokenTable[i].Type;
        nextToken.Value = sTokenTable[i].Value;

        bRetValue = true;
        pos = next;
        break;
      }
    }

    return bRetValue;
  }

  long Parse(const string& value)
  {
    long lResult = 0;
    int nNegatvie = 1;
    int nPos = string::npos;
    Token NextToken;
    int CurrentUnitValue = 1;

    while (GetPreToken(value, nPos, NextToken))
    {
      switch (NextToken.Type)
      {
      case TYPE_NEGATIVE:
        nNegatvie = -1;
        break;

      case TYPE_NUMBER:
        lResult += NextToken.Value * CurrentUnitValue;
        break;

      case TYPE_UNIT:
        if (NextToken.Value >= CurrentUnitValue)
        {
          CurrentUnitValue = NextToken.Value;
        }
        else
        {
          CurrentUnitValue *= NextToken.Value;
        }
        break;
      }

      if (nPos == string::npos)
      {
        break;
      }
    }

    lResult *= nNegatvie;

    return lResult;
  }

private:
  Token sTokenTable[32];
};